package com.community.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.community.common.core.utils.JWTUtils;
import com.community.common.core.utils.Result;
import com.community.gateway.properties.IgnorePathProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;

// 网关统一上线Token校验
@Component
@Slf4j
public class TokenFilter implements GlobalFilter {

    @Autowired
    private IgnorePathProperties ignorePathProperties;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();

        // 1.判断当前uri是否需要token的校验
        String url = request.getURI().getPath();
        log.debug("url:{}", url);
        boolean isIgnore = isIgnorePath(url);
        if (isIgnore) {
            // 当前请求不需要校验token
            return chain.filter(exchange);
        }

        // 2.获取token
        HttpHeaders headers = exchange.getRequest().getHeaders();
        String token = headers.getFirst("token");

        // 3.非空判断
        Result result = new Result();
        if (ObjectUtils.isEmpty(token)) {
            return errorInfo(exchange, result.error(420, "token不能为空")); // 这里应该响应420，代表没有登录
        }

        // 4.校验token的合法性
        DecodedJWT decodedJWT = null;
        try {
            decodedJWT = JWTUtils.checkToken(token);
        } catch (JWTVerificationException e) {
            log.error("token校验失败", e);
            return errorInfo(exchange, result.error(420, "重新登录")); // 这里应该响应420，代表没有登录
        }

        // 5.解析出Token中的用户的登录信息
        String loginUserJson = decodedJWT.getClaim("loginUser").asString();

        // 6.把登录用户信息往下传递就可以了
//        request.mutate().header("userInfo", loginUserJson);
        ServerHttpRequest newRequest = mutateHeader(loginUserJson, headers, request); // 创建一个新的请求对象，创建一个心的请求头

        // 7、往下放行
        return chain.filter(exchange.mutate().request(newRequest).build());
    }

    private ServerHttpRequest mutateHeader(String loginUserJson, HttpHeaders headers, ServerHttpRequest request) {
        // 1.创建一个新的请求头对象
        HttpHeaders newHttpHeaders = new HttpHeaders();

        // 2.把之前的请求头的数据添加到新的请求头对象中
        newHttpHeaders.putAll(headers);

        // 3.删除请求头中的token
        newHttpHeaders.remove("token");

        // 4.添加
        newHttpHeaders.add("userInfo", loginUserJson);

        // 5.创建一个新的请求对象，请求地址不能变
        ServerHttpRequest newRequest = request.mutate().uri(request.getURI()).build();

        // 6、把新的请求对象中添加新的请求头
        newRequest = new ServerHttpRequestDecorator(newRequest) {
            @Override
            public HttpHeaders getHeaders() {
                return newHttpHeaders;
            }
        };
        return newRequest;
    }

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    private boolean isIgnorePath(String url) {
        // 1.获取所有需要忽略的路径
        List<String> ignorePathList = ignorePathProperties.getIgnorePath();

        for (String path : ignorePathList) {
            if (antPathMatcher.match(path, url)) {
                return true;
            }
        }
        return false;
    }

    public Mono<Void> errorInfo(ServerWebExchange exchange, Result r) {
        return Mono.defer(() -> {
            // 把对象转成JSON
            byte[] bytes = bytes = JSON.toJSONBytes(r);
            // 获取响应对象
            ServerHttpResponse response = exchange.getResponse();
            // 响应响应类型
            response.getHeaders().add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
            // 设置HTTP响应码
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            DataBuffer buffer = response.bufferFactory().wrap(bytes);
            return response.writeWith(Flux.just(buffer));
        });
    }
}
